// Copyright (C) Calum Grant 2008

#include "cppscript"
#include "dynamic/extensions.hpp"

#include "fstat.hpp"

#include <algorithm>
#include <iostream>
#include <sstream>
#include <ctype.h>
#include <ctime>
#include <iterator>

#include "config_file.hpp"

var dynamic::system(const var & cmd)
{
	return ::system(cmd.as_string().c_str());
}


scopeguard::scopeguard(const var & v) : m_on_exit(v) 
{ 
}


scopeguard::~scopeguard() 
{ 
	try
	{
		m_on_exit(); 
	}
	catch(...)
	{
		// Silently ignore errors.  Not good.
	}
}


void scopeguard::set(const var & v)
{
	m_on_exit = v;
}


namespace
{
	void do_nothing() 
	{ 
	}

	struct my_to_string
	{
		std::string operator()(const var & v) const 
		{ 
			return v.as_string(); 
		}
	};

	struct my_to_cstr
	{
		const char * operator()(const std::string & s) 
		{ 
			return s.c_str(); 
		}
	};
}


void scopeguard::dismiss() 
{ 
	set(do_nothing);
}


var dynamic::exec(const var & cmd, const var & args)
{
	std::vector<std::string> string_args;
	std::vector<const char *> cstr_args;
	string_args.push_back(cmd.as_string());
	std::transform(args.begin(), args.end(), std::back_inserter(string_args), my_to_string());
	std::transform(string_args.begin(), string_args.end(), std::back_inserter(cstr_args), my_to_cstr());
	cstr_args.push_back(0);

	return api::execv(cmd.as_string().c_str(), (char**)&cstr_args[0]);
}


#if 0
var dynamic::regex(var str)
{
	return create<cg::wregex>( str.as_wstring() );
}


namespace
{
	var build_match(const wstring & str, const cg::sub_match<wstring::const_iterator> & sm)
	{
		return sm.matched ? 
			range(sm.first-str.begin(), sm.second-str.begin()) : 
			range(0,0);
	}

	var build_match_results(const wstring & str, const cg::wsmatch & mr)
	{
		var a = array();
		for(cg::wsmatch::const_iterator i=mr.begin(); i!=mr.end(); ++i)
		{
			a.push_back(build_match(str, *i));
		}
		return a;
	}
}


var dynamic::regex_match(var re, var str)
{
	cg::wsmatch mr;
	wstring ws = str.as_wstring();
	return cg::regex_match(ws, mr, re.as<cg::wregex>() ) ?
		build_match_results(ws, mr) : array();
}


var dynamic::regex_find(var re, var str)
{
	cg::wsmatch mr;
	wstring ws = str.as_wstring();
	return cg::regex_search(ws, mr, re.as<cg::wregex>() ) ?
		build_match_results(ws, mr) : array();
}


var dynamic::regex_find_all(var re, var str)
{
	var results = array();

	cg::wsmatch mr;
	wstring ws = str.as_wstring();

	for(cg::wsregex_iterator i=make_regex_iterator(ws, re.as<cg::wregex>() );
		i!=cg::wsregex_iterator(); ++i)
	{
		results.push_back( build_match_results(ws, *i) );
	}

	return results;
}
#endif


var dynamic::max(const var &a, const var &b)
{
	return a>b?a:b;
}


var dynamic::min(const var &a, const var &b)
{
	return a<b?a:b;
}


dynamic::string dynamic::internal::to_string(const var & v)
{
	std::basic_stringstream<char> ss;
	v.impl().output(ss);
	return ss.str();
}


dynamic::wstring dynamic::internal::to_wstring(const var & v)
{
	std::basic_stringstream<wchar_t> ss;
	v.impl().output(ss);
	return ss.str();
}


void dynamic::assertx( const var & b ) 
{ 
	if (!b) throw exception("assertion failed"); 
}


var dynamic::run_tests(const var & tests)
{
	var failures = 0;

	foreach( m, tests ) 
	{
		try
		{
			if(tests[m].max_args() == 0)
			{
				tests[m]();
				writeln( "PASSED: " + m );
			}
		}
		catch(var e)
		{
			++failures;
			writeln( "FAILED: " + m + ": " + e["text"] );
		}
		catch(...)
		{
			++failures;
			writeln( "FAILED: " + m );
		}
	}
	return failures;
}


var dynamic::substring(const var & str, const var & range)
{
	var sub = "";
	sub.impl().reserve( range.size() );
	foreach( i, range ) 
	{
		if(str.contains(i)) 
			sub.push_back(str[i]);
	}
	return sub;
}


namespace
{
	inline bool string_find_at(int pos, int substr_size, const var & str, const var & substr)
	{
		for(int j=0; j<substr_size; ++j)
			if( str[pos+j] != substr[j] ) return false;
		return true;
	}
}


var dynamic::string_find(const var & str, const var & substr)
{
	int str_size = str.size();
	int substr_size = substr.size();

	for(int i=0; i<str_size; ++i)
		if(string_find_at(i, substr_size, str, substr)) 
			return sequence( i, substr_size );

	return null;
}


var dynamic::string_find_last(const var & str, const var & substr)
{
	int str_size = str.size();
	int substr_size = substr.size();

	for(int i=str_size-1; i>=0; --i)
		if(string_find_at(i, substr_size, str, substr)) 
			return sequence( i, substr_size );

	return null;
}


var dynamic::string_find_all(const var & str, const var & substr)
{
	int str_size = str.size();
	int substr_size = substr.size();

	var results = array();

	for(int i=0; i<str_size; ++i)
		if(string_find_at(i, substr_size, str, substr)) 
		{
			results.push_back( sequence( i, substr_size ) );
		}

	return results;
}


var dynamic::split_chars(var predicate, var str)
{
	var results = array();
	var current_string = "";

	bool last_matched = false;

	foreach( ch, str )
	{
		if( predicate(ch) ) 
		{
			current_string.push_back(ch);
			last_matched = true;
		}
		else
		{
			if(last_matched)
			{
				results.push_back( current_string );
				current_string = "";
				last_matched = false;
			}
		}
	}

	if(last_matched) results.push_back(current_string);

	return results;
}


var dynamic::is_space(var ch)
{
	return ::isspace((unsigned char)ch.as_int());
}


var dynamic::is_one_of(var str, var ch)
{
	foreach( i, str ) if(i==ch) return true;
	return false;
}


var dynamic::is_not_one_of(var str, var ch)
{
	return !is_one_of(str, ch);
}


var dynamic::is_not(var predicate, var x)
{
	return !predicate(x);
}


double dynamic::timer()
{
	return clock()/double(CLOCKS_PER_SEC);
}


var dynamic::pad(const var & str, const var & size)
{
	var p = str.as_string();
	p.resize(size);
	return p;
}


var dynamic::read_user_config(var config_name, var default_value)
{
	return config_file().read(config_file::user, config_name, default_value);
}


var dynamic::read_system_config(var config_name, var default_value)
{
	return config_file().read(config_file::system, config_name, default_value);
}


void dynamic::write_user_config(var config_name, var value)
{
	config_file().write(config_file::user, config_name, value);
}


void dynamic::write_system_config(var config_name, var value)
{
	config_file().write(config_file::system, config_name, value);
}
